﻿/**
 * 
 * Copyright 2009, 2010 Mike Jamer [email: mikejamer@gmail.com ]
 * 
 * This File is part of Moose IRC
 * 
 * MooseIRC is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * MooseIRC is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with MooseIRC.  If not, see <http://www.gnu.org/licenses/>.
 * 
 */

using moose.extension;
using moose.irc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace moose.irc {
    
    /// <summary>
    /// Irc Client
    /// </summary>
    /// <remarks>
    /// need to come up with some kind of event system,
    /// to notify of various things, 
    /// this will help standardize how the client will handle 
    /// the various happenings.
    /// </remarks>
    [CLSCompliant(true)]
    public sealed class IrcClient : object {

        #region [Local Attributes]

        //TODO: Time has come to pull this appart, and clean it up some more...
        //Not sure if this is proto_5 or not...

        /// <summary>
        /// IRC Connection
        /// </summary>
        private IrcConnection m_connection;

        /// <summary>
        /// IRC Session State
        /// </summary>
        private IrcState m_state;

        /// <summary>
        /// Server Settings
        /// </summary>
        private Dictionary<IrcServerSettings, string> m_server_settings;

        /// <summary>
        /// Client Settings
        /// </summary>
        private Dictionary<IrcClientSettings, string> m_client_settings;

        /// <summary>
        /// IRC Channel Collection
        /// </summary>
        private IrcChannelCollection m_colChannels;

        /// <summary>
        /// Global Irc User Collection
        /// </summary>
        private IrcUserCollection m_colUsers;

        #endregion

        #region [Public Properties]

        /// <summary>
        /// Gets/Sets Irc Session State
        /// </summary>
        public IrcState SessionState {
            get {
                return m_state;
            }
            set {
                m_state = value;

                return;
            }
        }

        /// <summary>
        /// Gets/Sets Server Settings
        /// </summary>
        public Dictionary<IrcServerSettings, string> ServerSettings {
            get {
                return m_server_settings;
            }
            set {
                m_server_settings = value;
                return;
            }
        }

        /// <summary>
        /// Gets/Sets Server Settings
        /// </summary>
        public Dictionary<IrcClientSettings, string> ClientSettings {
            get {
                return m_client_settings;
            }
            set {
                m_client_settings = value;
                return;
            }
        }

        /// <summary>
        /// Gets/Sets Channels List
        /// </summary>
        public IrcChannelCollection Channels
        {
            get
            {
                return m_colChannels;
            }
            set
            {
                m_colChannels = value;
            }
        }

        #endregion

        #region [Events]

        /// <summary>
        /// Indicates a new message, that should be echoed into whatever is active
        /// </summary>
        public event ResponseHandler ActiveMessage;

        /// <summary>
        /// Event indicating a new status message
        /// </summary>
        public event ResponseHandler StatusMessage;

        /// <summary>
        /// Event indicating an IRC Event or Notification
        /// </summary>
        public event IrcEventHandler Event;

        #endregion

        #region [Construction]

        //TODO: Consider alternate constructors, to provide configuration information,
        //expecially important for automated registration

        /// <summary>
        /// Initialize IRC Client
        /// </summary>
        public IrcClient() {
            m_connection = new IrcConnection();
            m_server_settings = new Dictionary<IrcServerSettings, string>();
            m_client_settings = new Dictionary<IrcClientSettings, string>();
            m_colChannels = new IrcChannelCollection();
            m_colUsers = new IrcUserCollection();

            m_state = IrcState.Disconnected;
            m_connection.Response += onServerMessage;

            ActiveMessage += onActiveMessage;
            StatusMessage += onStatusMessage;
            Event += onEvent;

            return;
        }

        #endregion

        #region [Popular Methods]

        /// <summary>
        /// Connect to IRC Server (basic)
        /// </summary>
        /// <param name="host">host to lookup</param>
        /// <param name="port">port</param>
        public void connect(string host, int port) {
            //TODO: offload the sessionstate management from here, stick them in the standard extension
            m_connection.connect(host, port);
            if (m_connection.Connected) {
                SessionState = IrcState.Connected;
            } else {
                SessionState = IrcState.Disconnected;
            }
            return;
        }

        /// <summary>
        /// Disconnect Client from IRC Network
        /// </summary>
        public void disconnect() {
            //TODO: offload the sessionstate management from here, stick them in the standard extension
            if (m_connection.disconnect()) {
                SessionState = IrcState.Disconnected;
            } else {
                SessionState = IrcState.Connected;
            }
            return;
        }

        /// <summary>
        /// Echo text as a status message
        /// </summary>
        /// <param name="message">message to echo as status</param>
        public void echo(string message) {
            StatusMessage(message);
            return;
        }

        /// <summary>
        /// Echo text as an active message, which shoudl show up in whatever window is on top
        /// </summary>
        /// <param name="message">message to go into active window</param>
        public void echo_active(string message)
        {
            ActiveMessage(message);
            return;
        }
             
        /// <summary>
        /// Send to the client.
        /// If an action is found to match the command, 
        /// that action will be used.
        /// </summary>
        /// <param name="input">data to send</param>
        public void send(string input) {
            IrcMessage msg = new IrcMessage(input);
            IrcAction action = ExtensionManager.get_action(msg.Command, typeof(CommandAttribute));
            if (action != null) {
                action.execute(this, msg);
                return;
            }
            if (SessionState == IrcState.Registered //If we are registered
                || SessionState == IrcState.Connected) //Or connected
            {
                send_raw(input); //Send the input as it was recieved
            }
            return;
        }

        /// <summary>
        /// Send to the server,
        /// this bypasses the action / command lookup,
        /// and sends the data directly to the server
        /// </summary>
        /// <param name="input">data to send</param>
        public void send_raw(string input)
        {
            m_connection.send(input);
            return;
        }

        /// <summary>
        /// Notify of an event that has taken place
        /// </summary>
        /// <param name="irc_event">Irc Event Object</param>
        public void notify_event(IrcEvent irc_event)
        {
            Event(irc_event);
            return;
        }

        /// <summary>
        /// Gets a User from Global user list, or, creates that user and gets it.  Either way, good stuff...
        /// </summary>
        /// <param name="user_string"></param>
        /// <returns></returns>
        public IrcUser get_user(string user_string)
        {
            return m_colUsers.get_user(user_string);
        }

        #endregion

        #region [Message Processing]

        /// <summary>
        /// Process recieved message from server
        /// </summary>
        /// <param name="message">parsed message</param>
        private void dispatch(IrcMessage message) {
            if (message.Command == null) //If the message was not parsed correctly
                return; //Ignore it, quietly
            IrcAction action = ExtensionManager.get_action(message.Command, typeof(ResponseAttribute));
            if (action != null) { //If a response-action has been found...
                action.execute(this, message); //execute that action
            } else { //otherwise
                StatusMessage(concat_message(message)); //Send standard output as a status message
            }
            return;
        }

        //TODO: move this functionality into the message class itself, 
        //maybe provide different levels of printing, we could easily store the original message,
        //and then allow us to concat the command plus all arguments, or well, whatever...

        /// <summary>
        /// Concatinates input properly, mostly so that it can be printed as a status message more nicely...
        /// </summary>
        /// <param name="input">array of parameters</param>
        /// <returns>all input</returns>
        private string concat_message(IrcMessage message) {
            StringBuilder sb = new StringBuilder();
            sb.Append(message.Command); sb.Append(' ');
            foreach (string arg in message.Arguments) {
                sb.Append(arg); sb.Append(' ');
            }
            return sb.ToString().Trim();
        }

        #endregion

        #region [Event Handlers]

        /// <summary>
        /// Fires when the connection returns with a message from the server
        /// </summary>
        /// <param name="message">message from the server</param>
        private void onServerMessage(string message) 
        {
            dispatch(new IrcMessage(message));
            return;
        }

        /// <summary>
        /// fires on status message
        /// </summary>
        /// <param name="message">status message</param>
        /// <remarks>here primarily to prevent a null reference exception</remarks>
        private void onStatusMessage(string message) 
        {
            return;
        }

        /// <summary>
        /// Fires on active message
        /// </summary>
        /// <param name="message">active message</param>
        private void onActiveMessage(string message)
        {
            return;
        }

        /// <summary>
        /// fires on irc events
        /// </summary>
        /// <param name="irc_event_type">the event that has occurred</param>
        /// <param name="arguments">event arguments</param>
        private void onEvent(IrcEvent irc_event) {
            return;
        }

        #endregion

    }
}
